package com.kim.redis.common.lock.aspect;

import com.kim.redis.common.lock.annotation.KimLock;
import com.kim.redis.common.lock.core.KimLockModelProvider;
import com.kim.redis.common.lock.lock.RedisDistributionLockPlus;
import com.kim.redis.common.lock.model.KimLockModel;
import com.kim.redis.common.lock.utils.CurentMapUtils;
import com.kim.redis.common.lock.utils.ObjectUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @Project: kim-redis
 * @PackageName: com.kim.redis.common.lock.core
 * @FileName: KlockAspectHandler.java
 * @Description: 给添加@KimLock切面加锁处理
 * @Author: kimwu
 * @Time: 2020-12-19 14:08:56
 */
@Aspect
@Component
@Order(0)
@Slf4j
public class KimlockAspectHandler {


    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Pointcut("@annotation(com.kim.redis.common.lock.annotation.KimLock)")
    public void lockPointcut() {
        log.info("-------------------");
    }


    @Before("lockPointcut()")
    public Object around(JoinPoint joinPoint) throws Throwable {
        KimLock kimLock = getKimLock(joinPoint);
        KimLockModel kimLockModel = KimLockModelProvider.get(joinPoint, kimLock);
        if (ObjectUtils.isAllFieldNull(kimLockModel)) {
            throw new RuntimeException("对象不可为空");
        }
        //执行加锁过程
        RedisDistributionLockPlus redisDistributionLockPlus = new RedisDistributionLockPlus(stringRedisTemplate);
        CurentMapUtils.currentThreadLock.put(CurentMapUtils.getCurrentLockId(), redisDistributionLockPlus);
        boolean lock = redisDistributionLockPlus.lock(kimLockModel.getName(), kimLockModel.getValue(), kimLockModel.getExpireTime());
        log.info("{} 获取锁结果 {}", kimLockModel, lock);
        return joinPoint;
    }

    @AfterReturning(pointcut = "lockPointcut()")
    public void afterReturning(JoinPoint joinPoint) throws Throwable {
        KimLock kimLock = getKimLock(joinPoint);
        KimLockModel kimLockModel = KimLockModelProvider.get(joinPoint, kimLock);
        if (ObjectUtils.isAllFieldNull(kimLockModel)) {
            throw new RuntimeException("对象不可为空");
        }
        RedisDistributionLockPlus redisDistributionLockPlus = CurentMapUtils.currentThreadLock.get(CurentMapUtils.getCurrentLockId());

        boolean unlock = redisDistributionLockPlus.unlock(kimLockModel.getName(), kimLockModel.getValue());
        log.info("{} 释放锁结果 {}", kimLockModel, unlock);
    }

    @AfterThrowing(pointcut = "lockPointcut()", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Throwable e) throws Throwable {
        KimLock kimLock = getKimLock(joinPoint);
        KimLockModel kimLockModel = KimLockModelProvider.get(joinPoint, kimLock);
        if (ObjectUtils.isAllFieldNull(kimLockModel)) {
            throw new RuntimeException("对象不可为空");
        }
        RedisDistributionLockPlus redisDistributionLockPlus = CurentMapUtils.currentThreadLock.get(CurentMapUtils.getCurrentLockId());

        boolean unlock = redisDistributionLockPlus.unlock(kimLockModel.getName(), kimLockModel.getValue());
        log.info("{} 释放锁结果 {}", kimLockModel, unlock);
        throw e;
    }


    private KimLock getKimLock(JoinPoint joinPoint){
        KimLock kimLock = null;
        //获得所在切点的该类的class对象，也就是UserController这个类的对象
        Class<?> aClass = joinPoint.getTarget().getClass();
        //获取该切点所在方法的名称
        String name = joinPoint.getSignature().getName();
        try {
            //通过反射获得该方法
            Method method = aClass.getMethod(name);
            //获得该注解
            kimLock = method.getAnnotation(KimLock.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return kimLock;
    }
}
